/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#pragma once

#include <atomic>
#include <boost/asio.hpp>
#include <memory>
#include <queue>
#include <string>

#include "coin2/base/memory/mem_slice.h"
#include "coin2/base/tcp/tcp_connection.h"
#include "coin2/controller/base_type.h"
#include "coin2/controller/message_assembler.h"

namespace coin2::controller {
using TcpConnection = base::tcp::TcpConnection;

class TcpClient : public TcpConnection::Callback {
 public:
  explicit TcpClient(boost::asio::io_context *ioc,
                     MessageAssembler::Callback *callback);

  void Connect(std::string ip, uint16_t port);

  void SendMsg(memory::MemSlice buffer);

  // inherit from TcpConnection::Callback
  void OnConnect(std::weak_ptr<TcpConnection> conn, bool is_connect) override;
  void OnError(std::weak_ptr<TcpConnection> conn,
               boost::system::error_code ec) override;
  void OnRawDataReceived(std::weak_ptr<TcpConnection> conn,
                         memory::MemSlice buffer, uint32_t len) override;
  void OnDataSent(uint32_t len) override;

  void SetRegisterInfo(memory::MemSlice buffer);

  void RetryConnect();

 private:
  boost::asio::io_context *ioc_;
  boost::asio::deadline_timer timer_;
  std::shared_ptr<TcpConnection> tcp_connection_;
  std::string ip_;
  uint16_t port_;
  std::atomic<uint64_t> last_connect_ts_{0};

  std::queue<memory::MemSlice> q_;
  MessageAssembler msg_assembler_;

  memory::MemSlice register_info_;
};

inline void TcpClient::SetRegisterInfo(memory::MemSlice buffer) {
  register_info_ = std::move(buffer);
}

}  // namespace coin2::controller